#ifdef MAODV

/*****************************************************************************
 *
 * Copyright (C) 2003 CSHCN, University of Maryland.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Authors: Dionysus Blazakis <dblaze@umd.edu>
 *          Jonathan Polley <jpolley@umd.edu>
 *          Dan Rusk <drusk@umd.edu>
 *          Manish Karir <karir@umd.edu>
 *
 *****************************************************************************/

#include "k_mroute.h"
#include <stdio.h>    /* perror */
#include <unistd.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <linux/mroute.h>
#include <string.h>

/* Yanked from debug.h during a battle with the includes. */
#include <sys/syslog.h>
void log(int, int, const char *, char *, ...);

int multicast_socket = -1;

/* much of this code was taken from Dinesh's kernel MAODV
   implementation and cleaned up for user space.

   also, smc_route v.0.92 provided a good user space reference. */

/*
 *  kernel_mrt_init( )
 *  Create and initialize the multicast socket for communication with the kernel.
 */
int kernel_mrt_init( void )
{
    int error;
    int enable = 1;

    multicast_socket = socket( AF_INET, SOCK_RAW, IPPROTO_IGMP );

    /* error creating socket? */
    if ( multicast_socket < 0 ) {
        log(LOG_WARNING, 0, __FUNCTION__,
            "Failed to create socket.");
        return -1;
    }

    error = setsockopt( multicast_socket, IPPROTO_IP, MRT_INIT, 
        (int *) &enable, sizeof( enable ) );
    if ( error < 0 ) {
        log(LOG_WARNING, 0, __FUNCTION__,
            "Error starting kernel multicast routing.");
        return -1;
    }

    log(LOG_DEBUG, 0, __FUNCTION__,
        "Got the multicast kernel route socket!");
    
    return 0;
}

/*
 *  kernel_mrt_clean_up( )
 *  Close the socket and clean up the virtual interfaces.
 */
int kernel_mrt_clean_up( void )
{
    if(multicast_socket != -1) {

    int error = setsockopt( multicast_socket, IPPROTO_IP, MRT_DONE, 
        (char *) NULL, 0 );
    if ( error < 0 ) {
        log(LOG_WARNING, 0, __FUNCTION__,
            "Error finishing (MRT_DONE) kernel multicast routing.");
        return -1;
    }

    close( multicast_socket );

    log(LOG_DEBUG, 0, __FUNCTION__,
       "Killed multicast socket!");

    } /* if(multicast_socket != -1) { */
    return 0;
}

/*
 *  kernel_mrt_vif_add( addr, index )
 *  Adds a virtual interface from ip "addr" as index "index".
 */
int kernel_mrt_vif_add( u_int32_t this_ip, int index )
{
    if(multicast_socket != -1) {

    struct vifctl vc;
    int error;

    /* taken from smcroute-0.92 */
    vc.vifc_vifi = index;
    vc.vifc_flags = 0;
    vc.vifc_threshold = 1;
    vc.vifc_rate_limit = 0;
                
    vc.vifc_lcl_addr.s_addr = htonl(this_ip);
    vc.vifc_rmt_addr.s_addr = INADDR_ANY;

    error = setsockopt( multicast_socket, IPPROTO_IP, MRT_ADD_VIF, 
        (char *)&vc, sizeof(vc) ); 
    if ( error < 0 ) {
        perror("ERROR! " );
        log(LOG_WARNING, 0, __FUNCTION__,
            "Error creating virtual interface for kernel multicast routing[%d]." , error );
        return -1;
    }

    log(LOG_DEBUG, 0, __FUNCTION__,
        "Added virtual interface.");

    } /* if(multicast_socket != -1) { */
   return 0; 
}

/*
 *  kernel_mrt_vif_del( addr, index )
 *  Deletes a virtual interface at  from ip "addr" as index "index".
 */
int kernel_mrt_vif_del( u_int32_t this_ip, int index )
{
    if(multicast_socket != -1) {

    struct vifctl vc;
    int error;

    vc.vifc_vifi = (vifi_t) index;
    vc.vifc_flags = 0;
    // Is the threshold value 0 or 1??? - Dinesh
    vc.vifc_threshold = 0;
    vc.vifc_rate_limit = 0;
                
    vc.vifc_lcl_addr.s_addr = htonl(this_ip);
    vc.vifc_rmt_addr.s_addr = INADDR_ANY;

    error = setsockopt( multicast_socket, IPPROTO_IP, MRT_DEL_VIF, 
        (char *)&vc, sizeof(vc) ); 
    if ( error < 0 ) {
        log(LOG_WARNING, 0, __FUNCTION__,
            "Error deleting virtual interface for kernel multicast routing.");
        return -1;
    }

    } /* if(multicast_socket != -1) { */
    return 0; 
}

/*
 *  kernel_mrt_add_route( source, grp, vif )
 *  Add a multicast route to the kernel routing cache.
 */
int kernel_mrt_add_route( u_int32_t src, u_int32_t grp, int index )
{
    if(multicast_socket != -1) {

    struct mfcctl mc;
    int error;

    mc.mfcc_origin.s_addr = htonl(src);
    mc.mfcc_mcastgrp.s_addr = htonl(grp);
    mc.mfcc_parent = (vifi_t) index;
    memset(mc.mfcc_ttls, 0, sizeof(mc.mfcc_ttls));
    mc.mfcc_ttls[index] = 2;  /* CHECK: this seems odd... */

    error = setsockopt( multicast_socket, IPPROTO_IP, MRT_ADD_MFC, 
        (char *) ( &mc ), sizeof( mc ) );

    if ( error < 0 ) {
        perror( "ERROR " );
        log(LOG_WARNING, 0, __FUNCTION__,
            "Error adding route for kernel multicast routing.");
        return -1;
    }

    log(LOG_DEBUG, 0, __FUNCTION__,
        "Added multicast route cache entry.");
 
    } /* if(multicast_socket != -1) { */
    return 0;
}

/*
 *  kernel_mrt_del_route( source, grp, vif )
 *  Delete a multicast route from the kernel routing cache.
 */
int kernel_mrt_del_route( u_int32_t src, u_int32_t grp, int index )
{
    if(multicast_socket != -1) {

    struct mfcctl mc;
    int error;

    /* CHECK: ttls are diff in Dinesh's code... intentional? */
    mc.mfcc_origin.s_addr = htonl(src);
    mc.mfcc_mcastgrp.s_addr = htonl(grp);
    mc.mfcc_parent = (vifi_t) index;
    mc.mfcc_ttls[index] = 1;

    error = setsockopt( multicast_socket, IPPROTO_IP, MRT_DEL_MFC, 
        (char *) ( &mc ), sizeof( struct mfcctl ) );

    if ( error < 0 ) {
        log(LOG_WARNING, 0, __FUNCTION__,
            "Error deleting route for kernel multicast routing.");
        return -1;
    }

    log(LOG_DEBUG, 0, __FUNCTION__,
        "Removed multicast route cache entry.");
 
    } /* if(multicast_socket != -1) { */
    return 0;
}

#endif /* MAODV */
